
#include "Geosphere.h"

/*--------------------------------------------------------------------*/
Geosphere::Geosphere(float r, int l) {

	cout << "\nBuilding Geosphere..." << endl;

	radius = r;
	maxLevels = l;

	buildDiamondBase();
	cout << " - Recursive construction complete" << endl;

	for ( int i = 0 ; i < numFaces ; i++ ) {
		allFaces.push_back(&faces[i]);
		faces[i].collectFaces(allFaces);
	}
	cout << " - Vertex collection complete" << endl;

	list<GeosphereFace*>::iterator i;
	list<GeosphereFace*>::iterator j;

	int ii = 0;
	i = allFaces.begin();
	while ( i != allFaces.end() ) {
		j = allFaces.begin();
		int ji = 0;
		while ( ji++ < ii && j != allFaces.end() ) { ++j; }
		while ( j != allFaces.end() ) {
			if ( i != j ) { weldVerts(i,j); }
			++j;
		}
		++i;
		ii++;

		if ( ii%100 == 0 ) {
			cout << "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b - Welding... " << 
				(int)((ii/(float)allFaces.size())*100) << "%";
		}
	}
	cout << "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b";
	cout << " - Vertex welding complete" << endl;

	list<GeosphereVert*>::iterator k;
	for ( k = allVerts.begin() ; k != allVerts.end() ; ++k ) {
		(*k)->calcUnit();
	}
	cout << " - Normal creation complete" << endl;

	buildTerrain();
	cout << " - Terrain generation complete" << endl;

	cout << "Geosphere creation complete: " << 
		allVerts.size() << " vertices." << endl;

}

/*--------------------------------------------------------------------*/
void Geosphere::buildDiamondBase() {

	float r = radius;
	vec3f xPos = vec3f(  r,  0,  0);
	vec3f xNeg = vec3f( -r,  0,  0);
	vec3f yPos = vec3f(  0,  r,  0);
	vec3f yNeg = vec3f(  0, -r,  0);
	vec3f zPos = vec3f(  0,  0,  r);
	vec3f zNeg = vec3f(  0,  0, -r);

	numFaces = 8;
	faces = new GeosphereFace[numFaces];
	faces[0].setVerts( xPos, yNeg, zPos );
	faces[1].setVerts( yNeg, xNeg, zPos );
	faces[2].setVerts( xNeg, yPos, zPos );
	faces[3].setVerts( yPos, xPos, zPos );
	faces[4].setVerts( xPos, yNeg, zNeg );
	faces[5].setVerts( yNeg, xNeg, zNeg );
	faces[6].setVerts( xNeg, yPos, zNeg );
	faces[7].setVerts( yPos, xPos, zNeg );

	for ( int i = 0 ; i < numFaces ; i++ ) { 
		faces[i].doSplit(radius, maxLevels);
	}
}

/*--------------------------------------------------------------------*/
void Geosphere::buildCubeBase() {

	float rr = (float)sqrt( (radius*radius)/3.0 );
	vec3f nwt = vec3f( -rr, -rr, rr );
	vec3f net = vec3f(  rr, -rr, rr );
	vec3f swt = vec3f( -rr,  rr, rr );
	vec3f set = vec3f(  rr,  rr, rr );
	vec3f nwb = vec3f( -rr, -rr, -rr );
	vec3f neb = vec3f(  rr, -rr, -rr );
	vec3f swb = vec3f( -rr,  rr, -rr );
	vec3f seb = vec3f(  rr,  rr, -rr );

	numFaces = 12;
	faces = new GeosphereFace[numFaces];
	// Top
	faces[0].setVerts( nwt, net, swt );
	faces[1].setVerts( set, net, swt );
	// Bot
	faces[2].setVerts( nwb, neb, swb );
	faces[3].setVerts( seb, neb, swb );
	// North
	faces[4].setVerts( nwt, net, nwb );
	faces[5].setVerts( neb, net, nwb );
	// South
	faces[6].setVerts( swt, set, swb );
	faces[7].setVerts( seb, set, swb );
	// East
	faces[8].setVerts( net, set, neb );
	faces[9].setVerts( seb, set, neb );
	// East
	faces[10].setVerts( nwt, swt, nwb );
	faces[11].setVerts( swb, swt, nwb );

	for ( int i = 0 ; i < numFaces ; i++ ) { 
		faces[i].doSplit(radius, maxLevels);
	}
}

/*--------------------------------------------------------------------*/
inline void Geosphere::weldVerts(list<GeosphereFace*>::iterator& i, 
									list<GeosphereFace*>::iterator& j) {

	GeosphereFace* geoA = (*i);
	GeosphereFace* geoB = (*j);
	GeosphereVert** aVerts = geoA->getVerts();
	GeosphereVert** bVerts = geoB->getVerts();

	for ( register char a = 0 ; a < 3 ; a++ ) {
		for ( register char b = 0 ; b < 3 ; b++ ) {

			if ( aVerts[a] == bVerts[b] ) { continue; }

			// Check equality with room for precision issues
			if ( abs(aVerts[a]->vert.x - bVerts[b]->vert.x) > 0.0001 ) { continue; }
			if ( abs(aVerts[a]->vert.y - bVerts[b]->vert.y) > 0.0001 ) { continue; }
			if ( abs(aVerts[a]->vert.z - bVerts[b]->vert.z) > 0.0001 ) { continue; }

			delete bVerts[b];
			bVerts[b] = aVerts[a];

			if ( !geoA->getIsDivided() ) { aVerts[a]->adjFaces.push_back( geoA ); }			
			if ( !geoB->getIsDivided() ) { aVerts[a]->adjFaces.push_back( geoB ); }
			if ( !geoA->getIsDivided() || !geoB->getIsDivided() ) {
				aVerts[a]->adjFaces.sort();
				aVerts[a]->adjFaces.unique();
			}

			if ( !aVerts[a]->flag ) {
				allVerts.push_back( aVerts[a] );
				aVerts[a]->flag = true;
			}
		}
	}
}

/*--------------------------------------------------------------------*/
void Geosphere::buildTerrain() {

	list<GeosphereVert*>::iterator i;

	for ( int x = 1000 ; x > 0 ; x-- ) {

		vec3f pushDir = normalize(randVec());

		float pushAmt = (float)(x*0.000003*radius*(rand()%2?1:-1)); 
		float pushAngle = (float)(x*0.0003*radius);

		for ( i = allVerts.begin() ; i != allVerts.end() ; ++i ) {

			float angle = 1-dProd(pushDir, (*i)->unit);

			if ( angle > 0 && angle <= pushAngle ) {
				float newPushAmt = pushAmt*(angle/pushAngle);
				vec3f push = scaleVec( (*i)->unit, pushAmt );
				(*i)->vert = addVec( (*i)->vert, push );
			}
		}
	}
	
	list<GeosphereFace*>::iterator f;
	for ( f = allFaces.begin() ; f != allFaces.end() ; ++f ) {
		(*f)->calcFaceNorm();
	}

	for ( i = allVerts.begin() ; i != allVerts.end() ; ++i ) {
		(*i)->calcNorm();
	}
}

/*--------------------------------------------------------------------*/
int Geosphere::getNumFaces() { return numFaces; }
GeosphereFace* Geosphere::getFace(int i) { return &faces[i]; }
